package main;
import java.util.LinkedList;

public class DoubleObjectGrid extends TwoDGrid {
	
	/**
	 * Constructor. Takes the grid size as argument. This grid size should NOT
	 * include the borders.
	 * @param sizeX
	 * @param sizeY
	 * @throws InvalidArgumentException if sizeX or sizeY is <= 0
	 */
	public DoubleObjectGrid(int sizeX, int sizeY) {
		if(sizeX > 0 && sizeY > 0) {
			gridSizeX = sizeX;
			gridSizeY = sizeY;
			allPoints = new LinkedList();
			for(int i=0; i <= sizeX + 1; i++) {
				for(int j=0; j <= sizeY + 1; j++) {
					allPoints.add(new XYCoordinateWithdValue(i, j, 0));
				}
			}
			/* This is absurdly expensive. */
			for(int i=1; i < sizeX + 1; i++) {
				for(int j=1; j < sizeY + 1; j++) {
					XYCoordinateWithdValue me = (XYCoordinateWithdValue)allPoints.get(j + i*(gridSizeY + 2));
					me.setNeighbor(XYCoordinate.DIR_BOTTOM, (XYCoordinateWithdValue)allPoints.get(j + 1 + i*(gridSizeY + 2)));
					me.setNeighbor(XYCoordinate.DIR_TOP, (XYCoordinateWithdValue)allPoints.get(j - 1 + i*(gridSizeY + 2)));
					me.setNeighbor(XYCoordinate.DIR_RIGHT, (XYCoordinateWithdValue)allPoints.get(j + (i+1)*(gridSizeY + 2)));
					me.setNeighbor(XYCoordinate.DIR_LEFT, (XYCoordinateWithdValue)allPoints.get(j +  (i-1)*(gridSizeY + 2)));
				}
			}
			// Now deal with corners
			for(int j=1; j < sizeY + 1; j++) {
				// left side
				XYCoordinateWithdValue me = (XYCoordinateWithdValue)allPoints.get(j );
				me.setNeighbor(XYCoordinate.DIR_BOTTOM, (XYCoordinateWithdValue)allPoints.get(j + 1));
				me.setNeighbor(XYCoordinate.DIR_TOP, (XYCoordinateWithdValue)allPoints.get(j - 1));
				me.setNeighbor(XYCoordinate.DIR_RIGHT, (XYCoordinateWithdValue)allPoints.get(j + (gridSizeY + 2)));
			}
			
			for(int i=1; i < sizeX + 1; i++) {
				// Bottom
				XYCoordinateWithdValue me = (XYCoordinateWithdValue)allPoints.get(gridSizeY + 1 + i*(gridSizeY + 2));
				me.setNeighbor(XYCoordinate.DIR_TOP, (XYCoordinateWithdValue)allPoints.get(gridSizeY + i*(gridSizeY + 2)));
				me.setNeighbor(XYCoordinate.DIR_RIGHT, (XYCoordinateWithdValue)allPoints.get(gridSizeY + 1 + (i+1)*(gridSizeY + 2)));
				me.setNeighbor(XYCoordinate.DIR_LEFT, (XYCoordinateWithdValue)allPoints.get(gridSizeY + 1 +  (i-1)*(gridSizeY + 2)));
			}
			
			for(int j=1; j < sizeY + 1; j++) {
				// right side
				XYCoordinateWithdValue me = (XYCoordinateWithdValue)allPoints.get(j + (gridSizeX + 1)*(gridSizeY + 2));
				me.setNeighbor(XYCoordinate.DIR_BOTTOM, (XYCoordinateWithdValue)allPoints.get(j + 1 + (gridSizeX + 1)*(gridSizeY + 2)));
				me.setNeighbor(XYCoordinate.DIR_TOP, (XYCoordinateWithdValue)allPoints.get(j - 1 + (gridSizeX + 1)*(gridSizeY + 2)));
				me.setNeighbor(XYCoordinate.DIR_LEFT, (XYCoordinateWithdValue)allPoints.get(j +  gridSizeX*(gridSizeY + 2)));
			}
			
			for(int i=1; i < sizeX + 1; i++) {
				// Top
				XYCoordinateWithdValue me = (XYCoordinateWithdValue)allPoints.get(i*(gridSizeY + 2));
				me.setNeighbor(XYCoordinate.DIR_BOTTOM, (XYCoordinateWithdValue)allPoints.get(1 + i*(gridSizeY + 2)));
				me.setNeighbor(XYCoordinate.DIR_RIGHT, (XYCoordinateWithdValue)allPoints.get((i+1)*(gridSizeY + 2)));
				me.setNeighbor(XYCoordinate.DIR_LEFT, (XYCoordinateWithdValue)allPoints.get((i-1)*(gridSizeY + 2)));
			}
			
			corner1 = (XYCoordinateWithdValue)allPoints.get(0);
			corner1.setNeighbor(XYCoordinate.DIR_BOTTOM, (XYCoordinateWithdValue)allPoints.get(1));
			corner1.setNeighbor(XYCoordinate.DIR_RIGHT, (XYCoordinateWithdValue)allPoints.get(gridSizeY + 2));
			
			corner2 = (XYCoordinateWithdValue)allPoints.get(sizeX + 2 -1);
			corner2.setNeighbor(XYCoordinate.DIR_BOTTOM, (XYCoordinateWithdValue)allPoints.get(1 + (gridSizeX + 1)*(gridSizeY + 2)));
			corner2.setNeighbor(XYCoordinate.DIR_LEFT, (XYCoordinateWithdValue)allPoints.get((gridSizeX)*(gridSizeY + 2)));
			
			
			corner3 = (XYCoordinateWithdValue)allPoints.get((sizeX+2)*(sizeY+1) -1);
			corner3.setNeighbor(XYCoordinate.DIR_TOP, (XYCoordinateWithdValue)allPoints.get(gridSizeY));
			corner3.setNeighbor(XYCoordinate.DIR_RIGHT, (XYCoordinateWithdValue)allPoints.get(gridSizeY + 1 + (gridSizeY + 2)));
			
			
			corner4 = (XYCoordinateWithdValue)allPoints.get((sizeX + 2)*(sizeY+2) -1);
			corner4.setNeighbor(XYCoordinate.DIR_TOP, (XYCoordinateWithdValue)allPoints.get(gridSizeY + (gridSizeX + 1)*(gridSizeY + 2)));
			corner4.setNeighbor(XYCoordinate.DIR_LEFT, (XYCoordinateWithdValue)allPoints.get(gridSizeY + 1 +  gridSizeX*(gridSizeY + 2)));
			
			start = (XYCoordinateWithdValue)corner1.getNeighbor(XYCoordinate.DIR_BOTTOM);
			start = (XYCoordinateWithdValue)start.getNeighbor(XYCoordinate.DIR_RIGHT);
			
			end = (XYCoordinateWithdValue)corner4.getNeighbor(XYCoordinate.DIR_TOP);
			end = (XYCoordinateWithdValue)end.getNeighbor(XYCoordinate.DIR_LEFT);
			
			myAlgorithm = new FiniteDiffAlgorithmObjDouble(this);
		} else
			throw new java.lang.IllegalArgumentException("Grid sizes must be positive");
	}
	
	/**
	 * Convenience method to set all values on the border
	 * Equivalent to calling setValueAt over the borders
	 * @param which
	 * @param value
	 * @throws IllegalArgumentException if which is not SIDE_TOP, SIDE_LEFT
	 * SIDE_BOTTOM or SIDE_RIGHT
	 */
	public void initSideValue(int which, double value) {
		XYCoordinateWithdValue cur;
		switch(which) {
		case SIDE_LEFT:
			cur = corner1;
			cur = (XYCoordinateWithdValue)cur.getNeighbor(XYCoordinate.DIR_BOTTOM);
			for(int i=1; i < gridSizeY + 1; i++) {
				cur.setValue(value);
				cur =  (XYCoordinateWithdValue)cur.getNeighbor(XYCoordinate.DIR_BOTTOM);
			}
			break;
		case SIDE_TOP:
			cur = corner1;
			cur =  (XYCoordinateWithdValue)cur.getNeighbor(XYCoordinate.DIR_RIGHT);
			for(int i=1; i < gridSizeY + 1; i++) {
				cur.setValue(value);
				cur = (XYCoordinateWithdValue)cur.getNeighbor(XYCoordinate.DIR_RIGHT);
			}
			break;  
		case SIDE_RIGHT:
			cur = corner2;
			cur = (XYCoordinateWithdValue)cur.getNeighbor(XYCoordinate.DIR_BOTTOM);
			for(int i=1; i < gridSizeY + 1; i++) {
				cur.setValue(value);
        cur = (XYCoordinateWithdValue)cur.getNeighbor(XYCoordinate.DIR_BOTTOM);
			}
			break;
		case SIDE_BOTTOM:
			cur = corner3;
			cur =  (XYCoordinateWithdValue)cur.getNeighbor(XYCoordinate.DIR_RIGHT);
			for(int i=1; i < gridSizeY + 1; i++) {
				cur.setValue(value);
				cur = (XYCoordinateWithdValue)cur.getNeighbor(XYCoordinate.DIR_RIGHT);
			}
			break;  
		default:
			throw new java.lang.IllegalArgumentException("Invalid side");
		}
	}


	/* Inherited methods */
	public ICoordinate getNeighbor(ICoordinate coord, int direction) {
		if(coord instanceof XYCoordinate) 
			return coord.getNeighbor(direction);
		throw new java.lang.IllegalArgumentException("FloatArrayGrid expects XYCoordinates");
	}

	public boolean isDone() {
		if(myAlgorithm.getRelativeError() < 0.00001)
			return true;
		return false;
	}

	public String printCell(ICoordinate coord) {
		if(coord instanceof XYCoordinate) {
			XYCoordinate t = (XYCoordinate)coord;
			int x = t.getPositionX(), y = t.getPositionY();
			return decimalText.format(
        ((XYCoordinateWithdValue)allPoints.get(y + x*(gridSizeY + 2))).getValue() );
		}
		
		throw new java.lang.IllegalArgumentException("DoubleObjectGrid expects XYCoordinates");
	}

	public void stepGrid() {
		myAlgorithm.updateGrid(new XYCoordinateWithdValue[] {start, end});

	}

	
	public float getFloatValue(ICoordinate coord) {
		throw new java.lang.UnsupportedOperationException("Double object does not support floats");
	}

	
	public double getDoubleValue(ICoordinate coord) {
		XYCoordinate xy = (XYCoordinate)coord;
		return (
			((XYCoordinateWithdValue)allPoints.get(xy.getPositionY() + 
					xy.getPositionX()*(gridSizeY + 2))).getValue());
	}


	public void setFloatValue(ICoordinate coord, float value) {
		throw new java.lang.UnsupportedOperationException("Double object does not support floats");
		
	}


	public void setDoubleValue(ICoordinate coord, double value) {
		XYCoordinate xy = (XYCoordinate)coord;
		((XYCoordinateWithdValue)allPoints.get(xy.getPositionY() + 
				xy.getPositionX()*(gridSizeY + 2))).setValue(value);
		
	}

	

	public Double getDoubleObjValue(ICoordinate coord) {
		throw new java.lang.UnsupportedOperationException("Double object does not support double objects");
	}

	public Float getFloatObjValue(ICoordinate coord) {
		throw new java.lang.UnsupportedOperationException("Double object does not support float objects");
	}

	public void setDoubleObjValue(ICoordinate coord, Double value) {
		throw new java.lang.UnsupportedOperationException("Double object does not support double objects");
		
	}

	public void setFloatObjValue(ICoordinate coord, Float value) {
		throw new java.lang.UnsupportedOperationException("Double object does not support float objects");
		
	}
	
	/* Private variables */
	protected LinkedList allPoints;
	protected XYCoordinateWithdValue corner1, corner2, corner3, corner4; /* All corners */
	protected XYCoordinateWithdValue start, end;
	protected FiniteDiffAlgorithmObjDouble myAlgorithm;
}
